#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "util_udp.h"
#include "util-print.h"
#include "device_discovery.h"
#include "util_netintf_info.h"
#include "crc.h"
#include "config.h"
#include "iniparser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iniparser.h>

// 封装的接口函数，用于从 INI 文件中获取字符串配置
static int DevFind_GetConf(const char *name, char *value)
{
    dictionary *ini;
    const char *result;

    // 加载 INI 文件
    ini = iniparser_load(INI_PATH);
    if (ini == NULL)
    {
        //fprintf(stderr, "Failed to load INI file: %s\n", INI_PATH);
        return -1;
    }

    // 从 INI 文件中获取配置字符串
    result = iniparser_getstring(ini, name, NULL);
    if (result != NULL)
    {
        strcpy(value, result);
    }
    else
    {
        *value = '\0';
    }
    iniparser_freedict(ini);
    return result != NULL ? 0 : -1;
}

static int DevFind_PrepareData(char *fpga_version, char *soft_version, char *mac)
{
    int ret;
    ret = DevFind_GetConf("conf:fpga_version", fpga_version);
    if (ret < 0)
    {
        //printf("[ DEV FIND ] : get fpga version from %s failed\n", INI_PATH);
        strcpy(fpga_version, DEFAULT_FPGA_VERSION);
    }
    ret = DevFind_GetConf("conf:app_version", soft_version);
    if (ret < 0)
    {
        //printf("[ DEV FIND ] : get app version from %s failed\n", INI_PATH);
        strcpy(soft_version, DEFAULT_APP_VERSION);
    }

    ret = netintf_get_mac(ETH_INTF, (char *)mac);
    if (ret < 0)
    {
        memset(mac, 0xff, 6);
        //printf("[ DEV FIND ] : get %s mac failed\n", ETH_INTF);
    }
    return 0;
}

int main(void)
{
    UDP_INFO_T udp_info;
    msg_req_t *p_req;
    msg_rsp_t rsp = {0};
    msg_ack_t *p_ack;
    struct timeval start, curr, last_ack = {0};
    unsigned char state = STAT_IDLE;
    char buf[128];
    unsigned char retry = 0;
    unsigned short crc_calc;
    unsigned int elasped_ms = 0, elasped_sec = 0;
    char fpga_version[MAX_VERSION_LEN], soft_version[MAX_VERSION_LEN], mac[6];

    int ret = udp_init(&udp_info, NULL, UDP_LOCAL_PORT);
    if (ret < 0)
    {
        printf("[ DEV FIND ] : udp_server_init failed\n");
        return -1;
    }

    // 设置路由支持255.255.255.255广播地址
    system("route add -host 255.255.255.255 dev eth0");

    // 设置设备回复地址为受限广播地址(跨网段)
    udp_set_dst(&udp_info, "255.255.255.255", UDP_REMOTE_PORT);

    // 使能广播
    ret = udp_enable_broadcast(&udp_info);
    if (ret < 0)
    {
        printf("[ DEV FIND ] : udp_enable_broadcast failed\n");
        return -1;
    }

    while (1)
    {
        memset(buf, 0x00, sizeof(buf));
        // 阻塞等待上位机扫描请求,超时时间为1秒
        ret = udp_recv(&udp_info, buf, sizeof(buf), 1000);
        if (ret > 0)
        {
            printf("[ DEV FIND ] : recv request from %s:%d\n", inet_ntoa(udp_info.peer.sin_addr), ntohs(udp_info.peer.sin_port));
            // 调试信息,16进制打印出收到的数据帧
            // print_hex((unsigned char *)buf, ret);
            switch (state)
            {
            case STAT_IDLE:
                p_req = (msg_req_t *)buf;
                // 帧格式检查
                if (htonl(p_req->header) != MSG_FIXED_HEADER)
                {
                    printf("[ DEV FIND ] : wrong header at line %d\n", __LINE__);
                    break;
                }
                if (p_req->type != MSG_TYPE_REQ)
                {
                    printf("[ DEV FIND ] : wrong type at line %d\n", __LINE__);
                    break;
                }

                crc_calc = CRCCalc((unsigned char *)buf, sizeof(msg_req_t) - 2);
                if (htons(p_req->crc16) != crc_calc)
                {
                    printf("[ DEV FIND ] : wrong crc at line %d\n", __LINE__);
                    break;
                }
                // 收到正确的请求后,重试就从0计数
                retry = 0;
                gettimeofday(&start, NULL);
                if (last_ack.tv_sec > 0)
                {
                    elasped_sec = start.tv_sec - last_ack.tv_sec;
                    if (elasped_sec < REQUEST_INTERVAL_SEC)
                    {
                        printf("[ DEV FIND ] : receive repeat request,ignore it\n");
                        break;
                    }
                }
                memset(&rsp, 0x00, sizeof(rsp));
                // 填充设备信息并回复(广播)
                rsp.header = htonl(MSG_FIXED_HEADER);
                rsp.type = MSG_TYPE_RSP;
                rsp.port = htons(TFTP_PORT);
                //获取设备的基本信息
                DevFind_PrepareData(fpga_version, soft_version, mac);
                strncpy(rsp.fpga_version, fpga_version, MAX_VERSION_LEN);
                strncpy(rsp.soft_version, soft_version, MAX_VERSION_LEN);
                memcpy(rsp.mac, mac, 6);
                crc_calc = CRCCalc((unsigned char *)&rsp, sizeof(msg_rsp_t) - 2);
                rsp.crc16 = htons(crc_calc);

                ret = udp_send2dst(&udp_info, &rsp, sizeof(rsp));
                if (ret < 0)
                {
                    perror("send device info");
                    break;
                }
                state = STAT_SENT_RSP;
                printf("[ DEV FIND ] : received request and send response\n");
                break;

            case STAT_SENT_RSP:
                p_ack = (msg_ack_t *)buf;
                // 帧格式检查
                if (htonl(p_ack->header) != MSG_FIXED_HEADER)
                {
                    printf("[ DEV FIND ] : wrong header at line %d\n", __LINE__);
                    break;
                }
                if (p_ack->type != MSG_TYPE_ACK)
                {
                    printf("[ DEV FIND ] : wrong type at line %d\n", __LINE__);
                    break;
                }

                crc_calc = CRCCalc((unsigned char *)buf, sizeof(msg_ack_t) - 2);
                if (htons(p_ack->crc16) != crc_calc)
                {
                    printf("[ DEV FIND ] : wrong crc at line %d\n", __LINE__);
                    break;
                }

                // 上位机发送确认消息后,就代表本设备消息已经正确告知到了上位机,本设备状态回到最初态，方便可能的下次扫描
                state = STAT_IDLE;
                gettimeofday(&last_ack, NULL);
                printf("\n[ DEV FIND ] : receive ack,this device report info ok\n\n");
                break;
            }
        }

        // 上报了设备信息但是未收到上位机的回复
        if (state == STAT_SENT_RSP)
        {
            gettimeofday(&curr, NULL);
            elasped_ms = curr.tv_sec * 1000 - start.tv_sec * 1000 + curr.tv_usec / 1000 - start.tv_usec / 1000;
            if (elasped_ms >= (retry + 1) * 1000)
            {
                ret = udp_send2dst(&udp_info, &rsp, sizeof(rsp));
                if (ret < 0)
                {
                    perror("send device info retry");
                    break;
                }
                retry++;
                // 尝试3次发送设备信息，仍然收不到上位机回复,则认为上位机出现问题,设备回到最初态
                if (retry >= MAX_TRANSFRER_CNT)
                {
                    state = STAT_IDLE;
                    retry = 0;
                }
            }
        }
    }
    return 0;
}
